package cn.wolfcode.service.impl;

import cn.wolfcode.common.exception.BusinessException;
import cn.wolfcode.common.web.Result;
import cn.wolfcode.domain.OrderInfo;
import cn.wolfcode.domain.Product;
import cn.wolfcode.domain.SeckillProduct;
import cn.wolfcode.domain.SeckillProductVo;
import cn.wolfcode.feign.ProductApi;
import cn.wolfcode.mapper.OrderInfoMapper;
import cn.wolfcode.mapper.SeckillProductMapper;
import cn.wolfcode.redis.SeckillRedisKey;
import cn.wolfcode.service.ISeckillProductService;
import cn.wolfcode.util.IdGenerateUtil;
import cn.wolfcode.web.msg.SeckillCodeMsg;
import com.alibaba.fastjson.JSON;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;


@Service
public class SeckillProductServiceImpl implements ISeckillProductService {
    @Autowired
    private SeckillProductMapper seckillProductMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private ProductApi productApi;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Autowired
    private OrderInfoMapper orderInfoMapper;

    //通过time场次时间,查询相关数据库，封装为Vo对象
    @Override
    public List<SeckillProductVo> querySeckProductVOs(Long time) {
        //1、根据场次查出秒杀商品
        List<SeckillProduct> seckillProducts = seckillProductMapper.queryCurrentlySeckillProduct(time.intValue());
        //2、根据商品id集合远程调用product-service服务
        List<Long> ids = new ArrayList<>();
        for (SeckillProduct seckillProduct:seckillProducts) {
            ids.add(seckillProduct.getProductId());
        }
         Result<List<Product>> result= productApi.getProducts(ids);
        List<Product> products=result.getData();
        //3、聚合操作：封装为List<SeckillProductVO>
        //3.1将商品用map<productid,Product>进行封装，方便根据id获取
        Map<Long, Product> productHashMap = new HashMap<>();
        for (Product product:products) {
            productHashMap.put(product.getId(),product);
        }
        //3.2通过遍历秒杀商品集合，获取对应商品详情，封装VO对象并添加到List集合
        List<SeckillProductVo> seckillProductVos = new ArrayList<>();
        for (SeckillProduct seckillProduct:seckillProducts){
            SeckillProductVo seckillProductVo = new SeckillProductVo();
            Product product = productHashMap.get(seckillProduct.getProductId());//获取对应商品详情
            /**使用工具类注意先封装product,再封装seckillProduct;因为最后id必须为秒杀商品的id*/
            BeanUtils.copyProperties(product,seckillProductVo);
            BeanUtils.copyProperties(seckillProduct,seckillProductVo);

            seckillProductVos.add(seckillProductVo);
        }
        return seckillProductVos;
    }
    //从缓存中查出vo集合
    @Override
    public List<SeckillProductVo> querySeckProductVOsFromRedis(Long time) {
        List<SeckillProductVo> seckillProductVos=new ArrayList<>();
        //通过key获取hash中对应村的的value数据
        List<Object> stringVOs = stringRedisTemplate.opsForHash()
                .values(SeckillRedisKey.SECKILL_PRODUCT_LIST.getRealKey(time.toString()));
        //得到每一个vo对象
        for (Object stringVO:stringVOs) {
            SeckillProductVo seckillProductVo = JSON.parseObject(stringVO.toString(), SeckillProductVo.class);
            seckillProductVos.add(seckillProductVo);
        }
        return seckillProductVos;
    }

    //查询商品详情：根据time和seckillid从数据库查询相关数据，并封装为vo对象
    public SeckillProductVo getSeckillProductVO(Long time, Long seckillId) {
        SeckillProductVo seckillProductVo=null;
        //1、根据time和seckillid查询秒杀商品表并获取
        SeckillProduct seckillProduct=seckillProductMapper.querySeckillProductBytimeAndseckillId(time,seckillId);
        //2、根据秒杀商品的productId查询Product表s
        Result<Product> productResult=productApi.getProductById(seckillProduct.getProductId());
        Product product=productResult.getData();
        //3、聚合操作：封装VO对象(注意工具类，最后的id必须是秒杀商品的)
        seckillProductVo = new SeckillProductVo();
        BeanUtils.copyProperties(product,seckillProductVo);
        BeanUtils.copyProperties(seckillProduct,seckillProductVo);
        return seckillProductVo;
    }

    //秒杀之下单功能：修改秒杀商品真实库存-1；创建订单保存到mysql；
    // 为了避免重复下单：缓存（某场次，用户是否已经预购该秒杀商品）set结构：time [String类型phone:seckillId]
    public String doSeckill(Long seckillId, Long time, Long phone) {

        //下单逻辑：修改秒杀商品库存-1；创建订单；缓存足迹到redis
        int count = seckillProductMapper.decrStock(seckillId);//修改秒杀商品库存-1
        if(count==0){//小优化：影响的行。通过mysql数据库已经优化库存>0的才能操作成功
            throw new BusinessException(SeckillCodeMsg.SECKILL_STOCK_OVER);
        }
        OrderInfo orderInfo = new OrderInfo();
        String orderNo=createOrderInfo(orderInfo,seckillId,time,phone);//创建订单保存到mysql
        //缓存
        String hejiSet=phone.toString()+":"+seckillId.toString();
        redisTemplate.opsForSet().add(SeckillRedisKey.SECKILL_ORDER_SET.getRealKey(time.toString()),hejiSet);
        return orderNo;
    }

    @Override
    public SeckillProduct querySeckillProductBytimeAndseckillId(Long time, Long seckillId) {
        return seckillProductMapper.querySeckillProductBytimeAndseckillId(time,seckillId);
    }

    //查询所有秒杀商品
    public List<SeckillProduct> querySeckProducts() {
        List<SeckillProduct> seckillProducts = seckillProductMapper.querySeckills();
        return seckillProducts;
    }

    //回补预库存：从mysql中查出对应商品信息并将库存缓存到redis中
    public void initPreCountInRedis(Integer time, Long seckillId) {
        int stockCount = seckillProductMapper.getStockCount(seckillId);
        redisTemplate.opsForHash()
                .put(SeckillRedisKey.SECKILL_STOCK_COUNT_HASH.getRealKey(time.toString()),seckillId+"",stockCount+"");
    }

    //修改秒杀商品的库存+1
    public void incrStackCount(Long seckillId) {
        seckillProductMapper.incrStock(seckillId);
    }

    private String createOrderInfo(OrderInfo orderInfo,Long seckillId, Long time,Long phone) {
        SeckillProduct seckillProduct = seckillProductMapper.querySeckillProductBytimeAndseckillId(time, seckillId);
        Result<Product> productResult = productApi.getProductById(seckillProduct.getProductId());
        Product product=productResult.getData();
        //封装订单
        orderInfo.setUserId(phone);
        orderInfo.setCreateDate(new Date());
        orderInfo.setDeliveryAddrId(1L);//地址给个默认值
        orderInfo.setProductId(product.getId());
        orderInfo.setProductImg(product.getProductImg());
        orderInfo.setProductName(product.getProductName());
        orderInfo.setProductPrice(product.getProductPrice());
        orderInfo.setSeckillId(seckillId);
        orderInfo.setSeckillDate(new Date());
        orderInfo.setSeckillPrice(seckillProduct.getSeckillPrice());
        orderInfo.setSeckillTime(seckillProduct.getTime());//场次时间
        orderInfo.setProductCount(seckillProduct.getStockCount());//库存
        BigDecimal intergral = seckillProduct.getSeckillPrice().multiply(new BigDecimal("10"));
        orderInfo.setIntergral(intergral.longValue());//默认消耗积分:售价*10
        //雪花算法按顺序设置订单号
        orderInfo.setOrderNo(String.valueOf(IdGenerateUtil.get().nextId()));
        //保存到mysql
        orderInfoMapper.insert(orderInfo);
        return orderInfo.getOrderNo();
    }
}
